﻿using CodeGenerator.CSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Modifier = CodeGenerator.CSharp.Modifiers;

namespace CodeAngel.Domain
{
    /// <summary>
    /// Generates the DTO file for an EntityDef.  If this file already exists, it will
    /// be overwritten.
    /// </summary>
    public static class DtoFileGenerator
    {
        #region CreateDtoFileAsync

        /// <summary>
        /// Creates and writes to disk the DTO file for the specified EntityDef.
        /// If this file already exists, it will be overwritten.
        /// </summary>
        public static Task CreateDtoFileAsync(this EntityDef thisED)
        {
            //return PclDtoFileGenerator.WriteToDiskAsync(thisED);
            return Task.Factory.StartNew(() =>
            {
                DtoFileGenerator.WriteToDisk(thisED);
            });
        }

        #endregion

        #region WriteToDisk

        /// <summary>
        /// Creates and writes to disk the full-CLR DTO file. If this file already exists, 
        /// it will be overwritten.
        /// </summary>
        private static void WriteToDisk(EntityDef thisED)
        {
            var ThisFB = new FileBuilder();
            ThisFB.ProjectName = thisED.Domain.ProjectName;
            ThisFB.Directory = thisED.EntitiesDirectoryPath;
            ThisFB.FileName = thisED.FileNames.DtoDesigner();
            ThisFB.Namespace = thisED.Namespace;

            // Add the using namespaces for this file.
            ThisFB.UsingNamespaces.Add("System");
            ThisFB.UsingNamespaces.Add("System.Runtime.Serialization");
            ThisFB.UsingNamespaces.Add(thisED.Domain.FrameworkNamespace);

            // Add the DTO class.
            ThisFB.CodeUnits.Add(new DtoClassBuilder(thisED));

            // Write this file to disk.
            ThisFB.WriteToDisk(OverwriteExistingFile.Yes);
        }

        #endregion

        #region DtoClassBuilder

        /// <summary>
        /// Builds the DTO class.
        /// </summary>
        private class DtoClassBuilder : ClassBuilder
        {
            #region Constructors

            public DtoClassBuilder(EntityDef thisED)
                : base(thisED.ClassNames.Dto(), Modifier.Public)
            {
                this._ed = thisED;
                this.Attributes.Add("DataContract");
                this.Interfaces.Add("IDto");

                this.CreateIdProperty();
                this.CreateFieldProperties();
                this.CreateStateProperty();
            }
            private EntityDef _ed;

            #endregion

            #region CreateIdProperty

            /// <summary>
            /// Creates the ID property for the DTO and adds it to this ClassBuilder.
            /// </summary>
            private void CreateIdProperty()
            {
                var IdProperty = this.AddRegion("ID").AddProperty("ID", Modifier.Public, "Guid");
                IdProperty.Documentation.Summary = "Gets the unique identifier for this DTO.";
                IdProperty.GetBody.Add("return this." + this._ed.PrimaryKeyPropertyDef.Name + ";");
            }

            #endregion

            #region CreateFieldProperties

            /// <summary>
            /// Creates the various field properties, one for each PropertyDef in the current 
            /// EntityDef and adds it to this ClassBuilder.
            /// </summary>
            private void CreateFieldProperties()
            {

                var ThisRegion = this.AddRegion("Fields");

                foreach (var ThisPD in this._ed.PropertyDefs.ScalarProperties.OrderBy(i => i.Name))
                {
                    var ThisScalarProperty = ThisRegion.AddProperty(ThisPD.Name, Modifier.Public, ThisPD.Dt.ToDtoField());
                    ThisScalarProperty.IsAutoProperty = true;
                    ThisScalarProperty.Attributes.Add("DataMember");
                }

                foreach (var ThisPD in this._ed.PropertyDefs.EntityProperties.OrderBy(i => i.Name))
                {
                    string DataType = (ThisPD.IsNullable ? "Guid?" : "Guid");
                    var ThisEntityProperty = ThisRegion.AddProperty(ThisPD.Names.Field(), Modifier.Public, DataType);
                    ThisEntityProperty.IsAutoProperty = true;
                    ThisEntityProperty.Attributes.Add("DataMember");
                }

                var z = ThisRegion.AddProperty("TimeStamp", Modifier.Public, "byte[]");
                z.IsAutoProperty = true;
                z.Attributes.Add("DataMember");

            }


            #endregion

            #region CreateStateProperty

            /// <summary>
            /// Creates the State property for the DTO and adds it to this ClassBuilder.  This is
            /// used to track the editing state of the DTO.
            /// </summary>
            private void CreateStateProperty()
            {
                var StateProperty = this.AddRegion("State").AddProperty("State", Modifier.Public, "DtoChangedState");
                StateProperty.Attributes.Add("DataMember");
                StateProperty.Documentation.Summary = "Gets or sets the state of this dto.";
                StateProperty.IsAutoProperty = true;
            }

            #endregion

        }

        #endregion

    }

}
